package com.geek.gkfood.util.copy;

import lombok.extern.slf4j.Slf4j;
import org.springframework.cglib.beans.BeanCopier;
import org.springframework.objenesis.Objenesis;
import org.springframework.objenesis.ObjenesisStd;
import org.springframework.objenesis.instantiator.ObjectInstantiator;

import java.util.List;
import java.util.concurrent.ConcurrentHashMap;

/**
 * <h1>浅拷贝工具类。</h1><br/>
 *
 * <h1><br/><a href="https://u.geekbang.org/subject/arch3/100614201">Java 业务架构实战营：https://u.geekbang.org/subject/arch3/100614201</a></h1><br/><h1><a href="https://gitee.com/ylimhhmily/BizArchPracticeCamp">Java 业务架构实战营代码地址：https://gitee.com/ylimhhmily/BizArchPracticeCamp</a></h1><br/><h1><a href="https://u.geekbang.org/subject/java4th/1001148?source=app_share">高级 Java 工程体系课 2023 版：https://u.geekbang.org/subject/java4th/1001148?source=app_share</a></h1><br/><h1><a href="https://time.geekbang.org/column/intro/100312101?tab=catalog">Dubbo 源码剖析与实战：https://time.geekbang.org/column/intro/100312101?tab=catalog</a></h1>
 *
 * @author hmilyylimh
 * ^_^
 * @version 0.0.1
 * ^_^
 * @date 2022-02-18
 */
@Slf4j
public class ShallowCopyUtils {

    private static ConcurrentHashMap<String, BeanCopier> CACHE_MAP = new ConcurrentHashMap<>();

    /**
     * <h2>拷贝单个对象。</h2>
     *
     * @param source
     * @param target
     * @param sourceObj
     * @param useConverter
     * @param <T>
     * @return
     */
    public static <T> T copyBeanProperties(Class source, Class<T> target, Object sourceObj, boolean useConverter) {
        if (sourceObj == null) {
            return null;
        }
        T t;
        try {
            t = target.newInstance();
        } catch (Exception e) {
            log.error("", e);
            return null;
        }
        String key = source.getName() + target.getName();
        BeanCopier copier = CACHE_MAP.get(key);
        if (copier == null) {
            copier = createBeanCopier(source, target, useConverter, key);
        }
        copier.copy(sourceObj, t, null);
        return t;
    }

    /**
     * <h2>拷贝单个对象。</h2>
     *
     * @param sourceObj
     * @param target
     * @param <T>
     * @return
     */
    public static <T> T copyBeanProperties(Object sourceObj, T target) {
        return copyBeanProperties(sourceObj, target, false);
    }

    /**
     * <h2>拷贝单个对象。</h2>
     *
     * @param sourceObj
     * @param targetType
     * @param <T>
     * @return
     */
    public static <T> T copyToBeanByClazz(Object sourceObj, Class<T> targetType) {
        T t = null;
        try {
            t = targetType.newInstance();
        } catch (InstantiationException e) {
            log.error("", e);
        } catch (IllegalAccessException e) {
            log.error("", e);
        }
        return copyBeanProperties(sourceObj, t, false);
    }

    /**
     * <h2>拷贝单个对象。</h2>
     *
     * @param sourceObj
     * @param target
     * @param useConverter
     * @param <T>
     * @return
     */
    public static <T> T copyBeanProperties(Object sourceObj, T target, boolean useConverter) {
        if (sourceObj == null || target == null) {
            return null;
        }
        String key = sourceObj.getClass().getName() + target.getClass().getName();
        BeanCopier copier = CACHE_MAP.get(key);
        if (copier == null) {
            copier = createBeanCopier(sourceObj.getClass(), target.getClass(), useConverter, key);
        }
        copier.copy(sourceObj, target, null);
        return target;
    }

    /**
     * <h2>拷贝多个对象。</h2>
     *
     * @param sourceObjs
     * @param targets
     * @param targetType
     * @param <T>
     * @return
     */
    public static <T> List<T> copyListBeanPropertiesToList(List<?> sourceObjs, List<T> targets, Class<T> targetType) {
        if (sourceObjs == null || targets == null || targetType == null) {
            return null;
        }
        T t;
        for (Object o : sourceObjs) {
            try {
                t = targetType.newInstance();
                targets.add(copyBeanProperties(o, t, false));
            } catch (InstantiationException e) {
                log.error("", e);
            } catch (IllegalAccessException e) {
                log.error("", e);
            }
        }
        return targets;
    }

    private static BeanCopier createBeanCopier(Class sourceClass, Class targetClass, boolean useConverter,
                                               String cacheKey) {
        BeanCopier copier = BeanCopier.create(sourceClass, targetClass, useConverter);
        BeanCopier previous = CACHE_MAP.putIfAbsent(cacheKey, copier);
        if (previous != null) {
            copier = previous;
        }
        return copier;
    }

    /**
     * <h2>根据入参给定的 class 类型，创建出对应的对应类型的对象。</h2>
     *
     * @throws
     * @param: reqClz
     * @return: T
     * @author hmilyylimh
     * @date: 2023/9/3
     */
    public static <T> T newInstance(Class<?> reqClz) {
        Objenesis objenesis = new ObjenesisStd();
        ObjectInstantiator objectInstantiator = objenesis.getInstantiatorOf(reqClz);
        return (T) objectInstantiator.newInstance();
    }
}